package al.xc.common.util;

import com.google.common.base.Charsets;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;
import java.util.Map;
import java.util.TreeMap;

/**
 * 接口调用方验证
 */
public class VerifyUtils {

    public static final String SIGN_TYPE_MD5 = "MD5";
    public static final String SIGN_TYPE_HMAC_SHA256 = "HMAC-SHA256";

    /**
     * 加密key
     */
    protected String key = null;

    /**
     * 排序map
     */
    private Map<String, Object> values = new TreeMap<>(String::compareTo);

    /**
     * 构造函数
     * @param key
     */
    public VerifyUtils(String key) {
        this.key = key;
    }

    /**
     * 设置值
     * @param key
     * @param value
     */
    public void setValue(String key, Object value) {
        values.put(key, value);
    }

    /**
     * 获取值
     * @param key
     * @return
     */
    public Object getValue(String key) {
        return values.get(key);
    }

    /**
     * 获取字典
     * @return
     */
    public Map<String, Object> getValues() {
        return values;
    }

    /**
     * 判断某个字段是否已设置
     * @param key
     * @return
     */
    public boolean isSet(String key) {
        return values.containsKey(key);
    }

    /**
     * 将Dictionary转成xml
     * @return
     */
    public String toXml() {
        //数据为空时不能转化为xml格式
        if (0 == values.size()) {
            return null;
        }
        return XmlMapUtils.fromMap(values, null, true);
    }

    /**
     * 将xml转为WxPayData对象并返回对象内部的数据
     * @param xml
     * @return
     */
    public Map<String, Object> fromXml(String xml) {
        Map map = XmlMapUtils.fromXml(xml);
        values.clear();
        if (null == map) {
            return values;
        }
        values.putAll(map);
        return values;
    }

    /**
     * 检测签名是否正确
     * @param signType
     * @return
     */
    public boolean checkSign(String signType) {
        if (!isSet("sign")) {
            //如果没有设置签名，则跳过检测
            return false;
        }
        Object sign = getValue("sign");
        if (null == sign || sign.toString().equals("")) {
            //如果设置了签名但是签名为空，则抛异常
            return false;
        }
        //获取接收到的签名
        String return_sign = sign.toString();

        //在本地计算新的签名
        String cal_sign = makeSign(signType);

        if (cal_sign.equals(return_sign)) {
            return true;
        }
        return false;
    }

    /**
     * 生成签名，详见签名生成算法
     * @param signType
     * @return
     */
    public String makeSign(String signType) {
        //转url格式
        String str = toUrl();
        //在string后加入API KEY
        str += "&key=" + key;

        if (signType.equals(SIGN_TYPE_MD5)) {
            return CommonUtils.md5(str).toUpperCase();
        }
        else if (signType == SIGN_TYPE_HMAC_SHA256) {
            return calcHMACSHA256Hash(str, key).toUpperCase();
        }
        else {
            return null;
        }
    }

    /**
     * Dictionary格式转化成url参数格式
     * @return
     */
    public String toUrl() {
        StringBuilder builder = new StringBuilder();
        for(Map.Entry<String, Object> entry : values.entrySet()) {
            if (null == entry.getValue()) {
                return null;
            }
            if (!entry.getKey().equals("sign") && !entry.getValue().toString().equals("")) {
                builder.append(entry.getKey());
                builder.append("=");
                builder.append(entry.getValue());
                builder.append("&");
            }
        }
        String result = builder.toString();
        return result.substring(0, result.length() - 1);
    }

    /**
     * map格式化成Json
     * @return
     */
    public String toJson() {
        return GsonUtils.toJson(values);
    }

    /**
     * 将json格式化未map
     * @param json
     */
    public Map<String, Object> fromJson(String json) {
        Map map = GsonUtils.fromJson(json, Map.class);
        values.clear();
        if (null == map) {
            return values;
        }
        values.putAll(map);
        return values;
    }

    /**
     * 将model格式化未map
     * @param vl
     */
    public <T> void fromModel(T vl) {
        fromJson(GsonUtils.toJson(vl));
    }

    /**
     * values格式化成能在Web页面上显示的结果（因为web页面上不能直接输出xml格式的字符串）
     * @return
     */
    public String toPrintStr() {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, Object> entry : values.entrySet()) {
            if (entry.getValue() == null) {
                return null;
            }
            sb.append(entry.getKey());
            sb.append("=");
            sb.append(entry.getValue().toString());
            sb.append("\n");
        }
        try {
            return URLEncoder.encode(sb.toString(), "UTF-8");
        } catch (Exception ex) {
            return null;
        }
    }

    private String calcHMACSHA256Hash(String plaintext, String salt) {
        try {
            Mac mac = Mac.getInstance("HmacSHA256");
            SecretKeySpec spec = new SecretKeySpec(salt.getBytes(Charsets.UTF_8), "HmacSHA256");
            mac.init(spec);
            byte[] array = mac.doFinal(plaintext.getBytes(Charsets.UTF_8));
            StringBuilder sb = new StringBuilder();
            for (byte info : array) {
                sb.append(Integer.toHexString((info & 0xFF) | 0x100).substring(1, 3));
            }
            return sb.toString().toUpperCase();
        } catch (Exception ex) {
            return null;
        }
    }
}
